type TimeOutCallback = () => any

const timeouts: TimeOutCallback[] = []
const messageName = "zero-timeout-message"
window.addEventListener(
    "message",
    event => {
        if (event.source == window && event.data == messageName) {
            event.stopPropagation()
            const fn = timeouts.shift()
            fn?.()
        }
    },
    true
)

// Like setTimeout, but only takes a function argument.  There's
// no time argument (always zero) and no arguments (you have to
// use a closure).
export function emitAsyncMessage(fn: TimeOutCallback) {
    timeouts.push(fn)
    window.postMessage(messageName, "*")
}

// 将函数转换为Promise
export function async<R>(callback: () => R | PromiseLike<R>) {
    return new Promise<R>(resolve => {
        emitAsyncMessage(() => {
            const rs = callback()
            resolve(rs)
        })
    })
}

export function asyncMap<T, R>(callback: (t: T) => R) {
    return (t: T) =>
        new Promise<R>(resolve => {
            emitAsyncMessage(() => {
                const rs = callback(t)
                resolve(rs)
            })
        })
}

type CallbackFn<T, U> = (t: T) => U | PromiseLike<U>

export async function parallel<T, U>(array: Array<T>, concurrency: number, callback: CallbackFn<T, U>) {
    array = Array.from(array) // 复制到新数组
    const tasks: Promise<U>[] = [] // 用来存储所有的Promise任务
    const executing: Promise<void>[] = [] // 正在执行的Promise对象

    async function enqueue(): Promise<U | void> {
        const item = array.pop() // 获取参数
        if (!item) {
            return Promise.resolve()
        }

        const p = async(() => callback(item)) //不管callback是否为异步函数,都转换成异步函数
        tasks.push(p) // 放入promises数组
        // promise执行完毕，从executing数组中删除

        const e: Promise<void> = p.then(() => {
            executing.splice(executing.indexOf(e), 1)
        })
        // 插入executing数字，表示正在执行的promise
        executing.push(e)
        // 使用Promise.rece，每当executing数组中promise数量低于concurrency，就实例化新的promise并执行

        let next = executing.length >= concurrency ? Promise.race(executing) : Promise.resolve()
        // 递归，直到遍历完array
        await next
        return await enqueue()
    }
    await enqueue()
    return await Promise.all(tasks) // 最后呢，用Promise.all 等待全部任务完成，返回Promise的结果输出
}
